#pragma once
#ifndef QUEUE_H_
#define QUEUE_H_
#include<mutex>
#include<condition_variable>
#include<queue>
using namespace std;

template<typename T>
class Queue
{
public:
	Queue(){}
	~Queue(){}
	void Abort() {
		abort = 1;
		cond_t.notify_all();
	}
	int Push(T val) {
		lock_guard<mutex> lock(mutex_t);
		if (abort == 1) {
			return -1;
		}
		queue_t.push(val);
		cond_t.notify_one();
		return 0; 
	}
	int Pop(T& val, int timeout = 0) {
		unique_lock<mutex> lock(mutex_t);
		if (queue_t.empty()) {
			cond_t.wait_for(lock, chrono::milliseconds(timeout), [this] {
				return !queue_t.empty() | abort;
			});
		}
		if (abort == 1) {
			return -1;
		}
		if (queue_t.empty()) {
			return -2;
		}
		val = queue_t.front();
		queue_t.pop();
		return 0;
	}
	int Front(T& val) {
		lock_guard<mutex> lock(mutex_t);
		if (abort == 1) {
			return -1;
		}
		if (queue_t.empty()) {
			return -2;
		}
		val = queue_t.front();
		return 0;
	}

	int Size() {
		lock_guard<mutex> lock(mutex_t);
		return queue_t.size(); 
	}
private:
	int abort = 0;
	mutex mutex_t;
	condition_variable cond_t;
	queue<T> queue_t;
};

#endif